package com.xiaochao.bigdata.jdbc

import java.sql.{Connection, PreparedStatement, ResultSet, SQLException}
import java.util
import java.util.List

import com.mchange.v2.c3p0.ComboPooledDataSource
import com.xiaochao.bigdata.conf.ConfigurationManager
import com.xiaochao.bigdata.constant.Constants

class MDBManagerHelper extends Serializable {

  private val cpds: ComboPooledDataSource = new ComboPooledDataSource(true)
  try {

    cpds.setJdbcUrl(ConfigurationManager.getProperty(Constants.C3P0_JDBC_URL))
    cpds.setDriverClass(ConfigurationManager.getProperty(Constants.C3P0_JDBC_DRIVER))
    cpds.setUser(ConfigurationManager.getProperty(Constants.C3P0_JDBC_USER))
    cpds.setPassword(ConfigurationManager.getProperty(Constants.C3P0_JDBC_PASSWORD))
    cpds.setMaxPoolSize(Integer.valueOf(ConfigurationManager.getProperty(Constants.C3P0_JDBC_DATASOURCE_MAXPOOLSIZE)))
    cpds.setMinPoolSize(Integer.valueOf(ConfigurationManager.getProperty(Constants.C3P0_JDBC_DATASOURCE_MINPOOLSIZE)))
    cpds.setAcquireIncrement(Integer.valueOf(ConfigurationManager.getProperty(Constants.C3P0_JDBC_ACQUIREINCREMENT)))
    cpds.setInitialPoolSize(Integer.valueOf(ConfigurationManager.getProperty(Constants.C3P0_JDBC_INITIALPOOLSIZE)))
    cpds.setMaxIdleTime(Integer.valueOf(ConfigurationManager.getProperty(Constants.C3P0_JDBC_MAXIDLETIME)))

  } catch {
    case ex: Exception => ex.printStackTrace()
  }
  def getConnection():Connection={
    try {
       cpds.getConnection()
    } catch {
      case ex:Exception => ex.printStackTrace()
        null
    }
  }
  /**
    * 关闭数据库连接
    */
  def closeConn(connection: Connection){
    try {
      if(connection!=null && connection.isClosed()){
        connection.close();
      }
    } catch {

      case ex:SQLException=>ex.printStackTrace()

    }
  }

  def executeUpdate(sql: String, params: Array[AnyRef]): Int = {
    var rtn = 0
    var conn:Connection = null
    var pstmt:PreparedStatement = null
    try {
      conn = getConnection()
      pstmt = conn.prepareStatement(sql)
      var i = 0
      while ( {
        i < params.length
      }) {
        pstmt.setObject(i + 1, params(i))

        {
          i += 1; i - 1
        }
      }
      rtn = pstmt.executeUpdate
    } catch {
      case e: Exception =>
        e.printStackTrace()
    } finally if (conn != null) closeConn(conn)
    rtn
  }

  /**
    * 执行查询SQL语句
    *
    * @param sql
    * @param params
    * @param callback
    */
  def executeQuery(sql: String, params: Array[AnyRef], callback:ResultSet=>Unit): Unit = {
    var conn:Connection = null
    var pstmt:PreparedStatement = null
    var rs:ResultSet = null
    try {
      conn = getConnection()
      pstmt = conn.prepareStatement(sql)

      if (params.length > 0)
        for (i <- 0 to params.length -1) pstmt.setObject(i+1,params(i))



      rs = pstmt.executeQuery()
      //执行回调
      callback(rs)
    } catch {
      case e: Exception =>
        e.printStackTrace()
    } finally if (conn != null) closeConn(conn)
  }

  /**
    * 批量执行SQL语句
    *
    * 批量执行SQL语句，是JDBC中的一个高级功能
    * 默认情况下，每次执行一条SQL语句，就会通过网络连接，向MySQL发送一次请求
    *
    * 但是，如果在短时间内要执行多条结构完全一模一样的SQL，只是参数不同
    * 虽然使用PreparedStatement这种方式，可以只编译一次SQL，提高性能，但是，还是对于每次SQL
    * 都要向MySQL发送一次网络请求
    *
    * 可以通过批量执行SQL语句的功能优化这个性能
    * 一次性通过PreparedStatement发送多条SQL语句，比如100条、1000条，甚至上万条
    * 执行的时候，也仅仅编译一次就可以
    * 这种批量执行SQL语句的方式，可以大大提升性能
    *
    * @param sql
    * @param paramsList
    * @return 每条SQL语句影响的行数
    */
  def executeBatch(sql: String, paramsList: util.List[Array[AnyRef]]): Array[Int] = {
    var rtn: Array[Int] = null
    var conn: Connection = null
    var pstmt: PreparedStatement = null
    try {
      conn = getConnection
      // 第一步：使用Connection对象，取消自动提交
      conn.setAutoCommit(false)
      pstmt = conn.prepareStatement(sql)
      // 第二步：使用PreparedStatement.addBatch()方法加入批量的SQL参数
      import scala.collection.JavaConversions._
      for (params <- paramsList) {
        var i: Int = 0
        while ( {
          i < params.length
        }) {
          pstmt.setObject(i + 1, params(i))

          {
            i += 1; i - 1
          }
        }
        pstmt.addBatch()
      }
      // 第三步：使用PreparedStatement.executeBatch()方法，执行批量的SQL语句
      rtn = pstmt.executeBatch()
      // 最后一步：使用Connection对象，提交批量的SQL语句
      conn.commit()
    } catch {
      case e: Exception =>
        e.printStackTrace()
    }
    rtn
  }


}
object MDBManagerHelper{
  var mdbManager:MDBManagerHelper=_
  def getMDBManager():MDBManagerHelper={
    if (mdbManager == null)
      {
        synchronized{
          if(mdbManager==null){
            mdbManager = new MDBManagerHelper()
          }
        }
      }

    mdbManager
  }

  def main(args: Array[String]): Unit = {

   val connectionPool = getMDBManager();

    connectionPool.executeQuery("select name,age from test_user",Array[AnyRef](),(rs:ResultSet)=>{

      						while (rs.next()) {
      							val name = rs.getString("name");
                    val age = rs.getString("age");

      							// 匿名内部类的使用，有一个很重要的知识点
      							// 如果要访问外部类中的一些成员，比如方法内的局部变量
      							// 那么，必须将局部变量，声明为final类型，才可以访问
      							// 否则是访问不了的

                    println(name)
                    println(age)

                  }


    })


  }


}
